#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <atomic>
#include <fstream>
#include <algorithm>
#include <mutex>
#include <cassert>
#include <jsoncpp/json/json.h>
#include <httplib.h>
#include "../comm/log.hpp"
#include "../comm/util.hpp"
#include "oj_model.hpp"
#include "oj_view.hpp"

namespace NS_Controller
{
    using namespace httplib;
    using namespace NS_Log;
    using namespace NS_Util;
    using namespace NS_Model;
    using namespace NS_View;

    // 编译服务主机
    struct SvrMhin /* Server Machine */
    {
        std::string _ip;
        int _port;
        std::atomic<uint64_t> _load_factor; // 服务主机的负载情况

        SvrMhin(const SvrMhin& sm)
            : _ip(sm._ip), _port(sm._port), _load_factor(0)
        {
            // vector<server>插入元素会调用拷贝构造，atomic禁止拷贝构造
            uint64_t i = sm._load_factor;
            _load_factor = i;
        }

        SvrMhin() : _ip(""), _port(-1), _load_factor(0)
        {}
        ~SvrMhin()
        {}

        void IncFactor()
        {
            _load_factor++;
        }

        void DecFactor()
        {
            _load_factor--;
        }

        uint64_t GetFactor()
        {
            return _load_factor;
        }
    };

    const std::string svrs_conf_path = "./conf/server.conf";

    // 负载均衡模块
    class LoadBalance
    {
    public:
        LoadBalance()
        {
            assert(LoadConf());
            LOG(INFO) << "load severs conf success\n";
        }
        ~LoadBalance()
        {}

    public:
        bool LoadConf()
        {
            std::ifstream ifs(svrs_conf_path);
            if (!ifs.is_open())
            {
                LOG(FATAL) << "load servers machine conf file failed\n";
                return false;
            }

            std::string line;
            while (std::getline(ifs, line))
            {
                std::vector<std::string> tokens;
                StringUtil::SpiltString(line, &tokens, ":");
                if (tokens.size() != 2)
                {
                    LOG(WARNING) << "severs spilt conf content failed\n";
                    continue;
                }

                //构建server对象
                SvrMhin server;
                server._ip = tokens[0];
                server._port = std::stoi(tokens[1]);

                // 添加进servers数组
                _online_svrs.push_back(_servers.size()); // 记录该主机下标
                // _servers.push_back(server);
                _servers.push_back(std::move(server));
            }

            ifs.close();
            return true;
        }

        // 智能选择服务主机
        bool SmartSelection(int* id, SvrMhin** server)
        {
            std::lock_guard<std::mutex> lck(_mtx); // 维护负载选择的线程安全

            // 轮询检测
            int online_num = _online_svrs.size();  // 获取在线主机的个数
            if (online_num == 0)
            {
                LOG(FATAL) << "all server machines are offlined now, please check them\n";
                return false;
            }

            // 挑选负载最小的服务主机
            uint64_t min_load = _servers[_online_svrs[0]].GetFactor();
            *id = _online_svrs[0];
            *server = &_servers[_online_svrs[0]];

            for (int i = 1; i < online_num; i++)
            {
                uint64_t cur_load = _servers[_online_svrs[i]].GetFactor();
                if (min_load > cur_load)
                {
                    min_load = cur_load;
                    *id = _online_svrs[i];                // 获取该主机id
                    *server = &_servers[_online_svrs[i]]; // 获取该主机类地址
                }
            }

            return true;
        }

        void OfflineServer(int id)
        {
            std::lock_guard<std::mutex> lck(_mtx);
            for (auto it = _online_svrs.begin(); it != _online_svrs.end(); ++it)
            {
                if (*it == id)
                {
                    _online_svrs.erase(it);      // 将id从在线列表中取出
                    _offline_svrs.push_back(id); // 将id放到离线列表中

                    LOG(INFO) << "server: " << id << " is offline now\n";
                    break;
                }
            }

            ShowServersStatus();
        }

        void OnlineServer()
        {
            std::lock_guard<std::mutex> lck(_mtx);

            for (auto id : _offline_svrs)
                _online_svrs.push_back(id); // 离线列表中的id全部放到在线列表中
            _offline_svrs.clear();

            LOG(INFO) << "all offline server are online now\n";

            ShowServersStatus();
        }

        // for test
        void ShowServersStatus()
        {
            // std::lock_guard<std::mutex> lck(_mtx); // 死锁

            LOG(DEBUG) << "lastest online servers: ";
            for (auto& id : _online_svrs) std::cout << id << " ";
            std::cout << std::endl;

            LOG(DEBUG) << "lastest offline servers: ";
            for (auto& id : _offline_svrs) std::cout << id << " ";
            std::cout << std::endl;
        }

    private:
        std::vector<SvrMhin> _servers;   // 服务主机集合，使用数组下标作服务主机id
        std::vector<int> _online_svrs;  // 在线主机的下标集合
        std::vector<int> _offline_svrs; // 离线主机的下标集合
        std::mutex _mtx;
    };

    // 核心业务逻辑的控制器
    class Controller
    {
    private:
        Model _model;
        View _view;
        LoadBalance _load_balance; // 负载均衡器

    public:
        Controller()
        {}

        // 构建题库网页
        bool GetAllQuestionsHtml(std::string* html)
        {
            std::vector<Question> all;

            if (_model.GetAllQuestions(&all))
            {
                sort(all.begin(), all.end(), [](const Question& q1, const Question& q2) {
                    return std::stoi(q1._number) < std::stoi(q2._number);
                    }
                );
                // 成功获取所有题目，并将其构建网页
                _view.ExpandAllQuestionsHtml(all, html);
            }
            else
            {
                LOG(ERROR) << "expand all questions htmlfile failed\n";
                *html = "Expand Html failed";
                return false;
            }
            return true;
        }

        // 构建题目编辑网页
        bool GetOneQuestionHtml(const std::string& number, std::string* html)
        {
            Question question;
            if (_model.GetOneQuestion(number, &question))
            {
                // 成功获取题目，并将其构建网页
                _view.ExpandOneQuestionHtml(question, html);
            }
            else
            {
                LOG(ERROR) << "expand question" << number << "htmlfile failed\n";
                *html = "Expand Html failed";
                return false;
            }
            return true;
        }

        // in_json
        //   "code": "#incude...",
        //   "input": "...",
        void Judge(const std::string& number, const std::string& in_json, std::string* out_json)
        {
            Question question;
            _model.GetOneQuestion(number, &question); // 根据编号获取题目细节

            // 反序列化in_json，获取用户代码
            Json::Value in_value;
            Json::Reader reader;
            reader.parse(in_json, in_value);

            // 汇总用户代码和测试用例，形成out_json
            Json::Value out_value;
            out_value["input"] = in_value["input"].asString();
            out_value["code"] = in_value["code"].asString() + "\n" + question._tail;// 加\n
            out_value["cpu_lim"] = question._cpu_limit;
            out_value["mem_lim"] = question._mem_limit;
            Json::StyledWriter writer;
            std::string compile_string = writer.write(out_value);

            // 负载均衡选择主机
            while (true) // 一直选择到可用主机，否则就是全部挂掉
            {
                int id = 0;
                SvrMhin* server = nullptr;
                if (!_load_balance.SmartSelection(&id, &server))
                    break;

                // 作客户端对后台编译运行服务发起http请求
                Client cli(server->_ip, server->_port);
                server->IncFactor();

                LOG(INFO) << "select server success, id :" << id << " ip: " << server->_ip
                    << " port: " << server->_port << " fator: " << server->_load_factor << '\n';

                if (auto res = cli.Post(
                    "/compile_and_run",
                    compile_string, "application/json;charset=utf-8"))
                {
                    if (res->status == 200) // http状态码为200
                    {
                        *out_json = res->body; // 获取编译服务返回json串
                        server->DecFactor();

                        LOG(INFO) << "judge mudel resquest compile_and_run service success\n";
                        break;
                    }
                    server->DecFactor();
                }
                else // 请求失败
                {
                    LOG(ERROR) << "request compile_and_run server failed, id: "
                        << id << " ip: " << server->_ip << " port: " << server->_port << '\n';

                    _load_balance.OfflineServer(id);   // 记录离线主机id
                }
            }
        }

        void RecoveryServers()
        {
            _load_balance.OnlineServer();
        }

        ~Controller()
        {}

    };

}
